home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / fileutil / fileutils-3.16.tar.gz / fileutils-3.16.tar / fileutils-3.16 / lib / getdate.y < prev    next >
Text File  |  1997-01-18  |  25KB  |  1,025 lines

  1. %{
  2. /*
  3. **  Originally written by Steven M. Bellovin <smb@research.att.com> while
  4. **  at the University of North Carolina at Chapel Hill.  Later tweaked by
  5. **  a couple of people on Usenet.  Completely overhauled by Rich $alz
  6. **  <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
  7. **
  8. **  This grammar has 13 shift/reduce conflicts.
  9. **
  10. **  This code is in the public domain and has no copyright.
  11. */
  12.  
  13. #ifdef HAVE_CONFIG_H
  14. # include <config.h>
  15. # ifdef FORCE_ALLOCA_H
  16. #  include <alloca.h>
  17. # endif
  18. #endif
  19.  
  20. /* Since the code of getdate.y is not included in the Emacs executable
  21.    itself, there is no need to #define static in this file.  Even if
  22.    the code were included in the Emacs executable, it probably
  23.    wouldn't do any harm to #undef it here; this will only cause
  24.    problems if we try to write to a static variable, which I don't
  25.    think this code needs to do.  */
  26. #ifdef emacs
  27. # undef static
  28. #endif
  29.  
  30. #include <stdio.h>
  31. #include <ctype.h>
  32.  
  33. #if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII))
  34. # define IN_CTYPE_DOMAIN(c) 1
  35. #else
  36. # define IN_CTYPE_DOMAIN(c) isascii(c)
  37. #endif
  38.  
  39. #define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
  40. #define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
  41. #define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c))
  42. #define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
  43.  
  44. /* ISDIGIT differs from ISDIGIT_LOCALE, as follows:
  45.    - Its arg may be any int or unsigned int; it need not be an unsigned char.
  46.    - It's guaranteed to evaluate its argument exactly once.
  47.    - It's typically faster.
  48.    Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that
  49.    only '0' through '9' are digits.  Prefer ISDIGIT to ISDIGIT_LOCALE unless
  50.    it's important to use the locale's definition of `digit' even when the
  51.    host does not conform to Posix.  */
  52. #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
  53.  
  54. #include "getdate.h"
  55.  
  56. #if defined (STDC_HEADERS) || defined (USG)
  57. # include <string.h>
  58. #endif
  59.  
  60. /* Some old versions of bison generate parsers that use bcopy.
  61.    That loses on systems that don't provide the function, so we have
  62.    to redefine it here.  */
  63. #if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy)
  64. # define bcopy(from, to, len) memcpy ((to), (from), (len))
  65. #endif
  66.  
  67. extern struct tm    *gmtime ();
  68. extern struct tm    *localtime ();
  69. extern time_t        mktime ();
  70.  
  71. /* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc),
  72.    as well as gratuitiously global symbol names, so we can have multiple
  73.    yacc generated parsers in the same program.  Note that these are only
  74.    the variables produced by yacc.  If other parser generators (bison,
  75.    byacc, etc) produce additional global names that conflict at link time,
  76.    then those parser generators need to be fixed instead of adding those
  77.    names to this list. */
  78.  
  79. #define yymaxdepth gd_maxdepth
  80. #define yyparse gd_parse
  81. #define yylex   gd_lex
  82. #define yyerror gd_error
  83. #define yylval  gd_lval
  84. #define yychar  gd_char
  85. #define yydebug gd_debug
  86. #define yypact  gd_pact
  87. #define yyr1    gd_r1
  88. #define yyr2    gd_r2
  89. #define yydef   gd_def
  90. #define yychk   gd_chk
  91. #define yypgo   gd_pgo
  92. #define yyact   gd_act
  93. #define yyexca  gd_exca
  94. #define yyerrflag gd_errflag
  95. #define yynerrs gd_nerrs
  96. #define yyps    gd_ps
  97. #define yypv    gd_pv
  98. #define yys     gd_s
  99. #define yy_yys  gd_yys
  100. #define yystate gd_state
  101. #define yytmp   gd_tmp
  102. #define yyv     gd_v
  103. #define yy_yyv  gd_yyv
  104. #define yyval   gd_val
  105. #define yylloc  gd_lloc
  106. #define yyreds  gd_reds          /* With YYDEBUG defined */
  107. #define yytoks  gd_toks          /* With YYDEBUG defined */
  108. #define yylhs   gd_yylhs
  109. #define yylen   gd_yylen
  110. #define yydefred gd_yydefred
  111. #define yydgoto gd_yydgoto
  112. #define yysindex gd_yysindex
  113. #define yyrindex gd_yyrindex
  114. #define yygindex gd_yygindex
  115. #define yytable  gd_yytable
  116. #define yycheck  gd_yycheck
  117.  
  118. static int yylex ();
  119. static int yyerror ();
  120.  
  121. #define EPOCH        1970
  122. #define HOUR(x)        ((x) * 60)
  123.  
  124. #define MAX_BUFF_LEN    128   /* size of buffer to read the date into */
  125.  
  126. /*
  127. **  An entry in the lexical lookup table.
  128. */
  129. typedef struct _TABLE {
  130.     const char    *name;
  131.     int        type;
  132.     int        value;
  133. } TABLE;
  134.  
  135.  
  136. /*
  137. **  Meridian:  am, pm, or 24-hour style.
  138. */
  139. typedef enum _MERIDIAN {
  140.     MERam, MERpm, MER24
  141. } MERIDIAN;
  142.  
  143.  
  144. /*
  145. **  Global variables.  We could get rid of most of these by using a good
  146. **  union as the yacc stack.  (This routine was originally written before
  147. **  yacc had the %union construct.)  Maybe someday; right now we only use
  148. **  the %union very rarely.
  149. */
  150. static const char    *yyInput;
  151. static int    yyDayOrdinal;
  152. static int    yyDayNumber;
  153. static int    yyHaveDate;
  154. static int    yyHaveDay;
  155. static int    yyHaveRel;
  156. static int    yyHaveTime;
  157. static int    yyHaveZone;
  158. static int    yyTimezone;
  159. static int    yyDay;
  160. static int    yyHour;
  161. static int    yyMinutes;
  162. static int    yyMonth;
  163. static int    yySeconds;
  164. static int    yyYear;
  165. static MERIDIAN    yyMeridian;
  166. static int    yyRelDay;
  167. static int    yyRelHour;
  168. static int    yyRelMinutes;
  169. static int    yyRelMonth;
  170. static int    yyRelSeconds;
  171. static int    yyRelYear;
  172.  
  173. %}
  174.  
  175. %union {
  176.     int            Number;
  177.     enum _MERIDIAN    Meridian;
  178. }
  179.  
  180. %token    tAGO tDAY tDAY_UNIT tDAYZONE tDST tHOUR_UNIT tID
  181. %token    tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
  182. %token    tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE
  183.  
  184. %type    <Number>    tDAY tDAY_UNIT tDAYZONE tHOUR_UNIT tMINUTE_UNIT
  185. %type    <Number>    tMONTH tMONTH_UNIT
  186. %type    <Number>    tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE
  187. %type    <Meridian>    tMERIDIAN o_merid
  188.  
  189. %%
  190.  
  191. spec    : /* NULL */
  192.     | spec item
  193.     ;
  194.  
  195. item    : time {
  196.         yyHaveTime++;
  197.     }
  198.     | zone {
  199.         yyHaveZone++;
  200.     }
  201.     | date {
  202.         yyHaveDate++;
  203.     }
  204.     | day {
  205.         yyHaveDay++;
  206.     }
  207.     | rel {
  208.         yyHaveRel++;
  209.     }
  210.     | number
  211.     ;
  212.  
  213. time    : tUNUMBER tMERIDIAN {
  214.         yyHour = $1;
  215.         yyMinutes = 0;
  216.         yySeconds = 0;
  217.         yyMeridian = $2;
  218.     }
  219.     | tUNUMBER ':' tUNUMBER o_merid {
  220.         yyHour = $1;
  221.         yyMinutes = $3;
  222.         yySeconds = 0;
  223.         yyMeridian = $4;
  224.     }
  225.     | tUNUMBER ':' tUNUMBER tSNUMBER {
  226.         yyHour = $1;
  227.         yyMinutes = $3;
  228.         yyMeridian = MER24;
  229.         yyHaveZone++;
  230.         yyTimezone = ($4 < 0
  231.               ? -$4 % 100 + (-$4 / 100) * 60
  232.               : - ($4 % 100 + ($4 / 100) * 60));
  233.     }
  234.     | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
  235.         yyHour = $1;
  236.         yyMinutes = $3;
  237.         yySeconds = $5;
  238.         yyMeridian = $6;
  239.     }
  240.     | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
  241.         yyHour = $1;
  242.         yyMinutes = $3;
  243.         yySeconds = $5;
  244.         yyMeridian = MER24;
  245.         yyHaveZone++;
  246.         yyTimezone = ($6 < 0
  247.               ? -$6 % 100 + (-$6 / 100) * 60
  248.               : - ($6 % 100 + ($6 / 100) * 60));
  249.     }
  250.     ;
  251.  
  252. zone    : tZONE {
  253.         yyTimezone = $1;
  254.     }
  255.     | tDAYZONE {
  256.         yyTimezone = $1 - 60;
  257.     }
  258.     |
  259.       tZONE tDST {
  260.         yyTimezone = $1 - 60;
  261.     }
  262.     ;
  263.  
  264. day    : tDAY {
  265.         yyDayOrdinal = 1;
  266.         yyDayNumber = $1;
  267.     }
  268.     | tDAY ',' {
  269.         yyDayOrdinal = 1;
  270.         yyDayNumber = $1;
  271.     }
  272.     | tUNUMBER tDAY {
  273.         yyDayOrdinal = $1;
  274.         yyDayNumber = $2;
  275.     }
  276.     ;
  277.  
  278. date    : tUNUMBER '/' tUNUMBER {
  279.         yyMonth = $1;
  280.         yyDay = $3;
  281.     }
  282.     | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
  283.       /* Interpret as YYYY/MM/DD if $1 >= 1000, otherwise as MM/DD/YY.
  284.          The goal in recognizing YYYY/MM/DD is solely to support legacy
  285.          machine-generated dates like those in an RCS log listing.  If
  286.          you want portability, use the ISO 8601 format.  */
  287.       if ($1 >= 1000)
  288.         {
  289.           yyYear = $1;
  290.           yyMonth = $3;
  291.           yyDay = $5;
  292.         }
  293.       else
  294.         {
  295.           yyMonth = $1;
  296.           yyDay = $3;
  297.           yyYear = $5;
  298.         }
  299.     }
  300.     | tUNUMBER tSNUMBER tSNUMBER {
  301.         /* ISO 8601 format.  yyyy-mm-dd.  */
  302.         yyYear = $1;
  303.         yyMonth = -$2;
  304.         yyDay = -$3;
  305.     }
  306.     | tUNUMBER tMONTH tSNUMBER {
  307.         /* e.g. 17-JUN-1992.  */
  308.         yyDay = $1;
  309.         yyMonth = $2;
  310.         yyYear = -$3;
  311.     }
  312.     | tMONTH tUNUMBER {
  313.         yyMonth = $1;
  314.         yyDay = $2;
  315.     }
  316.     | tMONTH tUNUMBER ',' tUNUMBER {
  317.         yyMonth = $1;
  318.         yyDay = $2;
  319.         yyYear = $4;
  320.     }
  321.     | tUNUMBER tMONTH {
  322.         yyMonth = $2;
  323.         yyDay = $1;
  324.     }
  325.     | tUNUMBER tMONTH tUNUMBER {
  326.         yyMonth = $2;
  327.         yyDay = $1;
  328.         yyYear = $3;
  329.     }
  330.     ;
  331.  
  332. rel    : relunit tAGO {
  333.         yyRelSeconds = -yyRelSeconds;
  334.         yyRelMinutes = -yyRelMinutes;
  335.         yyRelHour = -yyRelHour;
  336.         yyRelDay = -yyRelDay;
  337.         yyRelMonth = -yyRelMonth;
  338.         yyRelYear = -yyRelYear;
  339.     }
  340.     | relunit
  341.     ;
  342.  
  343. relunit    : tUNUMBER tYEAR_UNIT {
  344.         yyRelYear += $1 * $2;
  345.     }
  346.     | tSNUMBER tYEAR_UNIT {
  347.         yyRelYear += $1 * $2;
  348.     }
  349.     | tYEAR_UNIT {
  350.         yyRelYear++;
  351.     }
  352.     | tUNUMBER tMONTH_UNIT {
  353.         yyRelMonth += $1 * $2;
  354.     }
  355.     | tSNUMBER tMONTH_UNIT {
  356.         yyRelMonth += $1 * $2;
  357.     }
  358.     | tMONTH_UNIT {
  359.         yyRelMonth++;
  360.     }
  361.     | tUNUMBER tDAY_UNIT {
  362.         yyRelDay += $1 * $2;
  363.     }
  364.     | tSNUMBER tDAY_UNIT {
  365.         yyRelDay += $1 * $2;
  366.     }
  367.     | tDAY_UNIT {
  368.         yyRelDay++;
  369.     }
  370.     | tUNUMBER tHOUR_UNIT {
  371.         yyRelHour += $1 * $2;
  372.     }
  373.     | tSNUMBER tHOUR_UNIT {
  374.         yyRelHour += $1 * $2;
  375.     }
  376.     | tHOUR_UNIT {
  377.         yyRelHour++;
  378.     }
  379.     | tUNUMBER tMINUTE_UNIT {
  380.         yyRelMinutes += $1 * $2;
  381.     }
  382.     | tSNUMBER tMINUTE_UNIT {
  383.         yyRelMinutes += $1 * $2;
  384.     }
  385.     | tMINUTE_UNIT {
  386.         yyRelMinutes++;
  387.     }
  388.     | tUNUMBER tSEC_UNIT {
  389.         yyRelSeconds += $1 * $2;
  390.     }
  391.     | tSNUMBER tSEC_UNIT {
  392.         yyRelSeconds += $1 * $2;
  393.     }
  394.     | tSEC_UNIT {
  395.         yyRelSeconds++;
  396.     }
  397.     ;
  398.  
  399. number    : tUNUMBER
  400.           {
  401.         if (yyHaveTime && yyHaveDate && !yyHaveRel)
  402.           yyYear = $1;
  403.         else
  404.           {
  405.         if ($1>10000)
  406.           {
  407.             yyHaveDate++;
  408.             yyDay= ($1)%100;
  409.             yyMonth= ($1/100)%100;
  410.             yyYear = $1/10000;
  411.           }
  412.         else
  413.           {
  414.             yyHaveTime++;
  415.             if ($1 < 100)
  416.               {
  417.             yyHour = $1;
  418.             yyMinutes = 0;
  419.               }
  420.             else
  421.               {
  422.                 yyHour = $1 / 100;
  423.                 yyMinutes = $1 % 100;
  424.               }
  425.             yySeconds = 0;
  426.             yyMeridian = MER24;
  427.           }
  428.           }
  429.       }
  430.     ;
  431.  
  432. o_merid    : /* NULL */
  433.       {
  434.         $$ = MER24;
  435.       }
  436.     | tMERIDIAN
  437.       {
  438.         $$ = $1;
  439.       }
  440.     ;
  441.  
  442. %%
  443.  
  444. /* Month and day table. */
  445. static TABLE const MonthDayTable[] = {
  446.     { "january",    tMONTH,  1 },
  447.     { "february",    tMONTH,  2 },
  448.     { "march",        tMONTH,  3 },
  449.     { "april",        tMONTH,  4 },
  450.     { "may",        tMONTH,  5 },
  451.     { "june",        tMONTH,  6 },
  452.     { "july",        tMONTH,  7 },
  453.     { "august",        tMONTH,  8 },
  454.     { "september",    tMONTH,  9 },
  455.     { "sept",        tMONTH,  9 },
  456.     { "october",    tMONTH, 10 },
  457.     { "november",    tMONTH, 11 },
  458.     { "december",    tMONTH, 12 },
  459.     { "sunday",        tDAY, 0 },
  460.     { "monday",        tDAY, 1 },
  461.     { "tuesday",    tDAY, 2 },
  462.     { "tues",        tDAY, 2 },
  463.     { "wednesday",    tDAY, 3 },
  464.     { "wednes",        tDAY, 3 },
  465.     { "thursday",    tDAY, 4 },
  466.     { "thur",        tDAY, 4 },
  467.     { "thurs",        tDAY, 4 },
  468.     { "friday",        tDAY, 5 },
  469.     { "saturday",    tDAY, 6 },
  470.     { NULL }
  471. };
  472.  
  473. /* Time units table. */
  474. static TABLE const UnitsTable[] = {
  475.     { "year",        tYEAR_UNIT,    1 },
  476.     { "month",        tMONTH_UNIT,    1 },
  477.     { "fortnight",    tDAY_UNIT,    14 },
  478.     { "week",        tDAY_UNIT,    7 },
  479.     { "day",        tDAY_UNIT,    1 },
  480.     { "hour",        tHOUR_UNIT,    1 },
  481.     { "minute",        tMINUTE_UNIT,    1 },
  482.     { "min",        tMINUTE_UNIT,    1 },
  483.     { "second",        tSEC_UNIT,    1 },
  484.     { "sec",        tSEC_UNIT,    1 },
  485.     { NULL }
  486. };
  487.  
  488. /* Assorted relative-time words. */
  489. static TABLE const OtherTable[] = {
  490.     { "tomorrow",    tMINUTE_UNIT,    1 * 24 * 60 },
  491.     { "yesterday",    tMINUTE_UNIT,    -1 * 24 * 60 },
  492.     { "today",        tMINUTE_UNIT,    0 },
  493.     { "now",        tMINUTE_UNIT,    0 },
  494.     { "last",        tUNUMBER,    -1 },
  495.     { "this",        tMINUTE_UNIT,    0 },
  496.     { "next",        tUNUMBER,    2 },
  497.     { "first",        tUNUMBER,    1 },
  498. /*  { "second",        tUNUMBER,    2 }, */
  499.     { "third",        tUNUMBER,    3 },
  500.     { "fourth",        tUNUMBER,    4 },
  501.     { "fifth",        tUNUMBER,    5 },
  502.     { "sixth",        tUNUMBER,    6 },
  503.     { "seventh",    tUNUMBER,    7 },
  504.     { "eighth",        tUNUMBER,    8 },
  505.     { "ninth",        tUNUMBER,    9 },
  506.     { "tenth",        tUNUMBER,    10 },
  507.     { "eleventh",    tUNUMBER,    11 },
  508.     { "twelfth",    tUNUMBER,    12 },
  509.     { "ago",        tAGO,    1 },
  510.     { NULL }
  511. };
  512.  
  513. /* The timezone table. */
  514. static TABLE const TimezoneTable[] = {
  515.     { "gmt",    tZONE,     HOUR ( 0) },    /* Greenwich Mean */
  516.     { "ut",    tZONE,     HOUR ( 0) },    /* Universal (Coordinated) */
  517.     { "utc",    tZONE,     HOUR ( 0) },
  518.     { "wet",    tZONE,     HOUR ( 0) },    /* Western European */
  519.     { "bst",    tDAYZONE,  HOUR ( 0) },    /* British Summer */
  520.     { "wat",    tZONE,     HOUR ( 1) },    /* West Africa */
  521.     { "at",    tZONE,     HOUR ( 2) },    /* Azores */
  522. #if    0
  523.     /* For completeness.  BST is also British Summer, and GST is
  524.      * also Guam Standard. */
  525.     { "bst",    tZONE,     HOUR ( 3) },    /* Brazil Standard */
  526.     { "gst",    tZONE,     HOUR ( 3) },    /* Greenland Standard */
  527. #endif
  528. #if 0
  529.     { "nft",    tZONE,     HOUR (3.5) },    /* Newfoundland */
  530.     { "nst",    tZONE,     HOUR (3.5) },    /* Newfoundland Standard */
  531.     { "ndt",    tDAYZONE,  HOUR (3.5) },    /* Newfoundland Daylight */
  532. #endif
  533.     { "ast",    tZONE,     HOUR ( 4) },    /* Atlantic Standard */
  534.     { "adt",    tDAYZONE,  HOUR ( 4) },    /* Atlantic Daylight */
  535.     { "est",    tZONE,     HOUR ( 5) },    /* Eastern Standard */
  536.     { "edt",    tDAYZONE,  HOUR ( 5) },    /* Eastern Daylight */
  537.     { "cst",    tZONE,     HOUR ( 6) },    /* Central Standard */
  538.     { "cdt",    tDAYZONE,  HOUR ( 6) },    /* Central Daylight */
  539.     { "mst",    tZONE,     HOUR ( 7) },    /* Mountain Standard */
  540.     { "mdt",    tDAYZONE,  HOUR ( 7) },    /* Mountain Daylight */
  541.     { "pst",    tZONE,     HOUR ( 8) },    /* Pacific Standard */
  542.     { "pdt",    tDAYZONE,  HOUR ( 8) },    /* Pacific Daylight */
  543.     { "yst",    tZONE,     HOUR ( 9) },    /* Yukon Standard */
  544.     { "ydt",    tDAYZONE,  HOUR ( 9) },    /* Yukon Daylight */
  545.     { "hst",    tZONE,     HOUR (10) },    /* Hawaii Standard */
  546.     { "hdt",    tDAYZONE,  HOUR (10) },    /* Hawaii Daylight */
  547.     { "cat",    tZONE,     HOUR (10) },    /* Central Alaska */
  548.     { "ahst",    tZONE,     HOUR (10) },    /* Alaska-Hawaii Standard */
  549.     { "nt",    tZONE,     HOUR (11) },    /* Nome */
  550.     { "idlw",    tZONE,     HOUR (12) },    /* International Date Line West */
  551.     { "cet",    tZONE,     -HOUR (1) },    /* Central European */
  552.     { "met",    tZONE,     -HOUR (1) },    /* Middle European */
  553.     { "mewt",    tZONE,     -HOUR (1) },    /* Middle European Winter */
  554.     { "mest",    tDAYZONE,  -HOUR (1) },    /* Middle European Summer */
  555.     { "mesz",    tDAYZONE,  -HOUR (1) },    /* Middle European Summer */
  556.     { "swt",    tZONE,     -HOUR (1) },    /* Swedish Winter */
  557.     { "sst",    tDAYZONE,  -HOUR (1) },    /* Swedish Summer */
  558.     { "fwt",    tZONE,     -HOUR (1) },    /* French Winter */
  559.     { "fst",    tDAYZONE,  -HOUR (1) },    /* French Summer */
  560.     { "eet",    tZONE,     -HOUR (2) },    /* Eastern Europe, USSR Zone 1 */
  561.     { "bt",    tZONE,     -HOUR (3) },    /* Baghdad, USSR Zone 2 */
  562. #if 0
  563.     { "it",    tZONE,     -HOUR (3.5) },/* Iran */
  564. #endif
  565.     { "zp4",    tZONE,     -HOUR (4) },    /* USSR Zone 3 */
  566.     { "zp5",    tZONE,     -HOUR (5) },    /* USSR Zone 4 */
  567. #if 0
  568.     { "ist",    tZONE,     -HOUR (5.5) },/* Indian Standard */
  569. #endif
  570.     { "zp6",    tZONE,     -HOUR (6) },    /* USSR Zone 5 */
  571. #if    0
  572.     /* For completeness.  NST is also Newfoundland Standard, and SST is
  573.      * also Swedish Summer. */
  574.     { "nst",    tZONE,     -HOUR (6.5) },/* North Sumatra */
  575.     { "sst",    tZONE,     -HOUR (7) },    /* South Sumatra, USSR Zone 6 */
  576. #endif    /* 0 */
  577.     { "wast",    tZONE,     -HOUR (7) },    /* West Australian Standard */
  578.     { "wadt",    tDAYZONE,  -HOUR (7) },    /* West Australian Daylight */
  579. #if 0
  580.     { "jt",    tZONE,     -HOUR (7.5) },/* Java (3pm in Cronusland!) */
  581. #endif
  582.     { "cct",    tZONE,     -HOUR (8) },    /* China Coast, USSR Zone 7 */
  583.     { "jst",    tZONE,     -HOUR (9) },    /* Japan Standard, USSR Zone 8 */
  584. #if 0
  585.     { "cast",    tZONE,     -HOUR (9.5) },/* Central Australian Standard */
  586.     { "cadt",    tDAYZONE,  -HOUR (9.5) },/* Central Australian Daylight */
  587. #endif
  588.     { "east",    tZONE,     -HOUR (10) },    /* Eastern Australian Standard */
  589.     { "eadt",    tDAYZONE,  -HOUR (10) },    /* Eastern Australian Daylight */
  590.     { "gst",    tZONE,     -HOUR (10) },    /* Guam Standard, USSR Zone 9 */
  591.     { "nzt",    tZONE,     -HOUR (12) },    /* New Zealand */
  592.     { "nzst",    tZONE,     -HOUR (12) },    /* New Zealand Standard */
  593.     { "nzdt",    tDAYZONE,  -HOUR (12) },    /* New Zealand Daylight */
  594.     { "idle",    tZONE,     -HOUR (12) },    /* International Date Line East */
  595.     {  NULL  }
  596. };
  597.  
  598. /* Military timezone table. */
  599. static TABLE const MilitaryTable[] = {
  600.     { "a",    tZONE,    HOUR (  1) },
  601.     { "b",    tZONE,    HOUR (  2) },
  602.     { "c",    tZONE,    HOUR (  3) },
  603.     { "d",    tZONE,    HOUR (  4) },
  604.     { "e",    tZONE,    HOUR (  5) },
  605.     { "f",    tZONE,    HOUR (  6) },
  606.     { "g",    tZONE,    HOUR (  7) },
  607.     { "h",    tZONE,    HOUR (  8) },
  608.     { "i",    tZONE,    HOUR (  9) },
  609.     { "k",    tZONE,    HOUR ( 10) },
  610.     { "l",    tZONE,    HOUR ( 11) },
  611.     { "m",    tZONE,    HOUR ( 12) },
  612.     { "n",    tZONE,    HOUR (- 1) },
  613.     { "o",    tZONE,    HOUR (- 2) },
  614.     { "p",    tZONE,    HOUR (- 3) },
  615.     { "q",    tZONE,    HOUR (- 4) },
  616.     { "r",    tZONE,    HOUR (- 5) },
  617.     { "s",    tZONE,    HOUR (- 6) },
  618.     { "t",    tZONE,    HOUR (- 7) },
  619.     { "u",    tZONE,    HOUR (- 8) },
  620.     { "v",    tZONE,    HOUR (- 9) },
  621.     { "w",    tZONE,    HOUR (-10) },
  622.     { "x",    tZONE,    HOUR (-11) },
  623.     { "y",    tZONE,    HOUR (-12) },
  624.     { "z",    tZONE,    HOUR (  0) },
  625.     { NULL }
  626. };
  627.  
  628.  
  629.  
  630.  
  631. /* ARGSUSED */
  632. static int
  633. yyerror (s)
  634.      char *s;
  635. {
  636.   return 0;
  637. }
  638.  
  639. static int
  640. ToHour (Hours, Meridian)
  641.      int Hours;
  642.      MERIDIAN Meridian;
  643. {
  644.   switch (Meridian)
  645.     {
  646.     case MER24:
  647.       if (Hours < 0 || Hours > 23)
  648.     return -1;
  649.       return Hours;
  650.     case MERam:
  651.       if (Hours < 1 || Hours > 12)
  652.     return -1;
  653.       if (Hours == 12)
  654.     Hours = 0;
  655.       return Hours;
  656.     case MERpm:
  657.       if (Hours < 1 || Hours > 12)
  658.     return -1;
  659.       if (Hours == 12)
  660.     Hours = 0;
  661.       return Hours + 12;
  662.     default:
  663.       abort ();
  664.     }
  665.   /* NOTREACHED */
  666. }
  667.  
  668. static int
  669. ToYear (Year)
  670.      int Year;
  671. {
  672.   if (Year < 0)
  673.     Year = -Year;
  674.  
  675.   /* XPG4 suggests that years 00-68 map to 2000-2068, and
  676.      years 69-99 map to 1969-1999.  */
  677.   if (Year < 69)
  678.     Year += 2000;
  679.   else if (Year < 100)
  680.     Year += 1900;
  681.  
  682.   return Year;
  683. }
  684.  
  685. static int
  686. LookupWord (buff)
  687.      char *buff;
  688. {
  689.   register char *p;
  690.   register char *q;
  691.   register const TABLE *tp;
  692.   int i;
  693.   int abbrev;
  694.  
  695.   /* Make it lowercase. */
  696.   for (p = buff; *p; p++)
  697.     if (ISUPPER (*p))
  698.       *p = tolower (*p);
  699.  
  700.   if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0)
  701.     {
  702.       yylval.Meridian = MERam;
  703.       return tMERIDIAN;
  704.     }
  705.   if (strcmp (buff, "pm") == 0 || strcmp (buff, "p.m.") == 0)
  706.     {
  707.       yylval.Meridian = MERpm;
  708.       return tMERIDIAN;
  709.     }
  710.  
  711.   /* See if we have an abbreviation for a month. */
  712.   if (strlen (buff) == 3)
  713.     abbrev = 1;
  714.   else if (strlen (buff) == 4 && buff[3] == '.')
  715.     {
  716.       abbrev = 1;
  717.       buff[3] = '\0';
  718.     }
  719.   else
  720.     abbrev = 0;
  721.  
  722.   for (tp = MonthDayTable; tp->name; tp++)
  723.     {
  724.       if (abbrev)
  725.     {
  726.       if (strncmp (buff, tp->name, 3) == 0)
  727.         {
  728.           yylval.Number = tp->value;
  729.           return tp->type;
  730.         }
  731.     }
  732.       else if (strcmp (buff, tp->name) == 0)
  733.     {
  734.       yylval.Number = tp->value;
  735.       return tp->type;
  736.     }
  737.     }
  738.  
  739.   for (tp = TimezoneTable; tp->name; tp++)
  740.     if (strcmp (buff, tp->name) == 0)
  741.       {
  742.     yylval.Number = tp->value;
  743.     return tp->type;
  744.       }
  745.  
  746.   if (strcmp (buff, "dst") == 0)
  747.     return tDST;
  748.  
  749.   for (tp = UnitsTable; tp->name; tp++)
  750.     if (strcmp (buff, tp->name) == 0)
  751.       {
  752.     yylval.Number = tp->value;
  753.     return tp->type;
  754.       }
  755.  
  756.   /* Strip off any plural and try the units table again. */
  757.   i = strlen (buff) - 1;
  758.   if (buff[i] == 's')
  759.     {
  760.       buff[i] = '\0';
  761.       for (tp = UnitsTable; tp->name; tp++)
  762.     if (strcmp (buff, tp->name) == 0)
  763.       {
  764.         yylval.Number = tp->value;
  765.         return tp->type;
  766.       }
  767.       buff[i] = 's';        /* Put back for "this" in OtherTable. */
  768.     }
  769.  
  770.   for (tp = OtherTable; tp->name; tp++)
  771.     if (strcmp (buff, tp->name) == 0)
  772.       {
  773.     yylval.Number = tp->value;
  774.     return tp->type;
  775.       }
  776.  
  777.   /* Military timezones. */
  778.   if (buff[1] == '\0' && ISALPHA (*buff))
  779.     {
  780.       for (tp = MilitaryTable; tp->name; tp++)
  781.     if (strcmp (buff, tp->name) == 0)
  782.       {
  783.         yylval.Number = tp->value;
  784.         return tp->type;
  785.       }
  786.     }
  787.  
  788.   /* Drop out any periods and try the timezone table again. */
  789.   for (i = 0, p = q = buff; *q; q++)
  790.     if (*q != '.')
  791.       *p++ = *q;
  792.     else
  793.       i++;
  794.   *p = '\0';
  795.   if (i)
  796.     for (tp = TimezoneTable; tp->name; tp++)
  797.       if (strcmp (buff, tp->name) == 0)
  798.     {
  799.       yylval.Number = tp->value;
  800.       return tp->type;
  801.     }
  802.  
  803.   return tID;
  804. }
  805.  
  806. static int
  807. yylex ()
  808. {
  809.   register char c;
  810.   register char *p;
  811.   char buff[20];
  812.   int Count;
  813.   int sign;
  814.  
  815.   for (;;)
  816.     {
  817.       while (ISSPACE (*yyInput))
  818.     yyInput++;
  819.  
  820.       if (ISDIGIT (c = *yyInput) || c == '-' || c == '+')
  821.     {
  822.       if (c == '-' || c == '+')
  823.         {
  824.           sign = c == '-' ? -1 : 1;
  825.           if (!ISDIGIT (*++yyInput))
  826.         /* skip the '-' sign */
  827.         continue;
  828.         }
  829.       else
  830.         sign = 0;
  831.       for (yylval.Number = 0; ISDIGIT (c = *yyInput++);)
  832.         yylval.Number = 10 * yylval.Number + c - '0';
  833.       yyInput--;
  834.       if (sign < 0)
  835.         yylval.Number = -yylval.Number;
  836.       return sign ? tSNUMBER : tUNUMBER;
  837.     }
  838.       if (ISALPHA (c))
  839.     {
  840.       for (p = buff; (c = *yyInput++, ISALPHA (c)) || c == '.';)
  841.         if (p < &buff[sizeof buff - 1])
  842.           *p++ = c;
  843.       *p = '\0';
  844.       yyInput--;
  845.       return LookupWord (buff);
  846.     }
  847.       if (c != '(')
  848.     return *yyInput++;
  849.       Count = 0;
  850.       do
  851.     {
  852.       c = *yyInput++;
  853.       if (c == '\0')
  854.         return c;
  855.       if (c == '(')
  856.         Count++;
  857.       else if (c == ')')
  858.         Count--;
  859.     }
  860.       while (Count > 0);
  861.     }
  862. }
  863.  
  864. #define TM_YEAR_ORIGIN 1900
  865.  
  866. /* Yield A - B, measured in seconds.  */
  867. static long
  868. difftm (a, b)
  869.      struct tm *a, *b;
  870. {
  871.   int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
  872.   int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
  873.   long days = (
  874.   /* difference in day of year */
  875.         a->tm_yday - b->tm_yday
  876.   /* + intervening leap days */
  877.         + ((ay >> 2) - (by >> 2))
  878.         - (ay / 100 - by / 100)
  879.         + ((ay / 100 >> 2) - (by / 100 >> 2))
  880.   /* + difference in years * 365 */
  881.         + (long) (ay - by) * 365
  882.   );
  883.   return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
  884.         + (a->tm_min - b->tm_min))
  885.       + (a->tm_sec - b->tm_sec));
  886. }
  887.  
  888. time_t
  889. get_date (p, now)
  890.      const char *p;
  891.      const time_t *now;
  892. {
  893.   struct tm tm, tm0, *tmp;
  894.   time_t Start;
  895.  
  896.   yyInput = p;
  897.   Start = now ? *now : time ((time_t *) NULL);
  898.   tmp = localtime (&Start);
  899.   yyYear = tmp->tm_year + TM_YEAR_ORIGIN;
  900.   yyMonth = tmp->tm_mon + 1;
  901.   yyDay = tmp->tm_mday;
  902.   yyHour = tmp->tm_hour;
  903.   yyMinutes = tmp->tm_min;
  904.   yySeconds = tmp->tm_sec;
  905.   yyMeridian = MER24;
  906.   yyRelSeconds = 0;
  907.   yyRelMinutes = 0;
  908.   yyRelHour = 0;
  909.   yyRelDay = 0;
  910.   yyRelMonth = 0;
  911.   yyRelYear = 0;
  912.   yyHaveDate = 0;
  913.   yyHaveDay = 0;
  914.   yyHaveRel = 0;
  915.   yyHaveTime = 0;
  916.   yyHaveZone = 0;
  917.  
  918.   if (yyparse ()
  919.       || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
  920.     return -1;
  921.  
  922.   tm.tm_year = ToYear (yyYear) - TM_YEAR_ORIGIN + yyRelYear;
  923.   tm.tm_mon = yyMonth - 1 + yyRelMonth;
  924.   tm.tm_mday = yyDay + yyRelDay;
  925.   if (yyHaveTime || (yyHaveRel && !yyHaveDate && !yyHaveDay))
  926.     {
  927.       tm.tm_hour = ToHour (yyHour, yyMeridian);
  928.       if (tm.tm_hour < 0)
  929.     return -1;
  930.       tm.tm_min = yyMinutes;
  931.       tm.tm_sec = yySeconds;
  932.     }
  933.   else
  934.     {
  935.       tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
  936.     }
  937.   tm.tm_hour += yyRelHour;
  938.   tm.tm_min += yyRelMinutes;
  939.   tm.tm_sec += yyRelSeconds;
  940.   tm.tm_isdst = -1;
  941.   tm0 = tm;
  942.  
  943.   Start = mktime (&tm);
  944.  
  945.   if (Start == (time_t) -1)
  946.     {
  947.  
  948.       /* Guard against falsely reporting errors near the time_t boundaries
  949.          when parsing times in other time zones.  For example, if the min
  950.          time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead
  951.          of UTC, then the min localtime value is 1970-01-01 08:00:00; if
  952.          we apply mktime to 1970-01-01 00:00:00 we will get an error, so
  953.          we apply mktime to 1970-01-02 08:00:00 instead and adjust the time
  954.          zone by 24 hours to compensate.  This algorithm assumes that
  955.          there is no DST transition within a day of the time_t boundaries.  */
  956.       if (yyHaveZone)
  957.     {
  958.       tm = tm0;
  959.       if (tm.tm_year <= EPOCH - TM_YEAR_ORIGIN)
  960.         {
  961.           tm.tm_mday++;
  962.           yyTimezone -= 24 * 60;
  963.         }
  964.       else
  965.         {
  966.           tm.tm_mday--;
  967.           yyTimezone += 24 * 60;
  968.         }
  969.       Start = mktime (&tm);
  970.     }
  971.  
  972.       if (Start == (time_t) -1)
  973.     return Start;
  974.     }
  975.  
  976.   if (yyHaveDay && !yyHaveDate)
  977.     {
  978.       tm.tm_mday += ((yyDayNumber - tm.tm_wday + 7) % 7
  979.              + 7 * (yyDayOrdinal - (0 < yyDayOrdinal)));
  980.       Start = mktime (&tm);
  981.       if (Start == (time_t) -1)
  982.     return Start;
  983.     }
  984.  
  985.   if (yyHaveZone)
  986.     {
  987.       long delta = yyTimezone * 60L + difftm (&tm, gmtime (&Start));
  988.       if ((Start + delta < Start) != (delta < 0))
  989.     return -1;        /* time_t overflow */
  990.       Start += delta;
  991.     }
  992.  
  993.   return Start;
  994. }
  995.  
  996. #if    defined (TEST)
  997.  
  998. /* ARGSUSED */
  999. int
  1000. main (ac, av)
  1001.      int ac;
  1002.      char *av[];
  1003. {
  1004.   char buff[MAX_BUFF_LEN + 1];
  1005.   time_t d;
  1006.  
  1007.   (void) printf ("Enter date, or blank line to exit.\n\t> ");
  1008.   (void) fflush (stdout);
  1009.  
  1010.   buff[MAX_BUFF_LEN] = 0;
  1011.   while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0])
  1012.     {
  1013.       d = get_date (buff, (time_t *) NULL);
  1014.       if (d == -1)
  1015.     (void) printf ("Bad format - couldn't convert.\n");
  1016.       else
  1017.     (void) printf ("%s", ctime (&d));
  1018.       (void) printf ("\t> ");
  1019.       (void) fflush (stdout);
  1020.     }
  1021.   exit (0);
  1022.   /* NOTREACHED */
  1023. }
  1024. #endif /* defined (TEST) */
  1025.